<template>
  <div class="wrap">
    <div id="map"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import * as maptalks from "maptalks";
import { ThreeLayer } from "maptalks.three";
import buildings from "./buildings.json";
import * as dat from "dat.gui";

export default {
  name: "threeMapTalk",
  data() {
    return {
      meshs: null,
      features: null,
      material: null,
      topColor: null,
      threeLayer: null,
    };
  },
  methods: {
    init() {
      // const map = new maptalks.Map('map', { /* options */ });
        // 地图底图
      const map = new maptalks.Map("map", {
        center: [13.414049129456203, 52.53160987202452],
        zoom: 17,
        pitch: 70,
        bearing: 180,

        // centerCross: true,
        doubleClickZoom: false,
        baseLayer: new maptalks.TileLayer("tile", {
          urlTemplate:
            "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
          subdomains: ["a", "b", "c", "d"],
          attribution:
            '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>',
        }),
      });

      // features to draw
      this.features = [];

      buildings.forEach((b) => {
        this.features = this.features.concat(b.features);
      });
      // the ThreeLayer to draw buildings   // three 图层
      this.threeLayer = new ThreeLayer("t", {
        forceRenderOnMoving: true,
        forceRenderOnRotating: true,
        // animation: true
      });

      this.meshs = [];
      this.material = this.getBuildingsMaterial();
      this.material.vertexColors = THREE.VertexColors;
      this.topColor = new THREE.Color("rgb(200, 200, 250)");

      this.threeLayer.prepareToDraw = (gl, scene, camera) => {
        const light = new THREE.DirectionalLight(0xffffff);
        light.position.set(0, -10, 10).normalize();
        scene.add(light);
        camera.add(new THREE.PointLight("#fff", 2));

        const heightPerLevel = 10;
        const polygons = this.features.map((f) => {
          const polygon = maptalks.GeoJSON.toGeometry(f);
          const levels = f.properties.levels || 1;
          polygon.setProperties({
            height: heightPerLevel * levels,
          });
          return polygon;
        });

        const mesh = this.threeLayer.toExtrudePolygons(
          polygons,
          { interactive: false },
          this.material
        );

        const bufferGeometry = mesh.getObject3d().geometry;
        const geometry = new THREE.Geometry().fromBufferGeometry(
          bufferGeometry
        );

        const { vertices, faces, faceVertexUvs } = geometry;
        for (let i = 0, len = faces.length; i < len; i++) {
          const { a, b, c } = faces[i];
          const p1 = vertices[a],
            p2 = vertices[b],
            p3 = vertices[c];
          //top face
          if (p1.z > 0 && p2.z > 0 && p3.z > 0) {
            const vertexColors = faces[i].vertexColors;
            for (let j = 0, len1 = vertexColors.length; j < len1; j++) {
              vertexColors[j].r = this.topColor.r;
              vertexColors[j].g = this.topColor.g;
              vertexColors[j].b = this.topColor.b;
            }
            const uvs = faceVertexUvs[0][i];
            for (let j = 0, len1 = uvs.length; j < len1; j++) {
              uvs[j].x = 0;
              uvs[j].y = 0;
            }
          }
        }
        mesh.getObject3d().geometry = new THREE.BufferGeometry().fromGeometry(
          geometry
        );
        bufferGeometry.dispose();
        geometry.dispose();
        // const position = geometry.attributes.position.array;
        // const uv = geometry.attributes.uv.array;
        // const index = geometry.index.array;
        // for (let i = 0, len = index.length; i < len; i += 3) {
        //     const a = index[i], b = index[i + 1], c = index[i + 2];
        //     const z1 = position[a * 3 + 2], z2 = position[b * 3 + 2], z3 = position[c * 3 + 2];
        //     // top vertex
        //     if (z1 > 0 && z2 > 0 && z3 > 0) {
        //         uv[a * 2] = 0;
        //         uv[a * 2 + 1] =  0;
        //         uv[b * 2] =  0;
        //         uv[b * 2 + 2] =  0;
        //         uv[c * 2] =  0;
        //         uv[c * 2 + 1] =  0;
        //     }
        // }
        this.meshs.push(mesh);
        this.threeLayer.addMesh(this.meshs);
        this.initGui();
        this.animation();
      },
        this.threeLayer.addTo(map);
    },
    // getBuildingsMaterial(color = 'red') {
    getBuildingsMaterial() {
      const texture = new THREE.TextureLoader().load(
        "static/textures/building2.png"
      );
      texture.needsUpdate = true; //使用贴图时进行更新
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      // texture.repeat.set(0.002, 0.002);
      texture.repeat.set(1, 4);
      const material = new THREE.MeshPhongMaterial({
        map: texture,
        transparent: true,
        color: "#fff",
      });
      return material;
    },
    animation() {
      // layer animation support Skipping frames
      this.threeLayer._needsUpdate = !this.threeLayer._needsUpdate;
      if (this.threeLayer._needsUpdate) {
        this.threeLayer.renderScene();
      }
      requestAnimationFrame(this.animation);
    },
    initGui() {
      const params = {
        add: true,
        color: this.material.color.getStyle(),
        show: true,
        opacity: 1,
        altitude: 0,
        animateShow: this.animateShow,
      };
      const gui = new dat.GUI();
      gui.add(params, "add").onChange(() => {
        if (params.add) {
          this.threeLayer.addMesh(this.meshs);
        } else {
          this.threeLayer.removeMesh(this.meshs);
        }
      });
      gui
        .addColor(params, "color")
        .name("building color")
        .onChange(() => {
          this.material.color.set(params.color);
          this.meshs.forEach((mesh) => {
            mesh.setSymbol(this.material);
          });
        });
      gui.add(params, "opacity", 0, 1).onChange(() => {
        this.material.opacity = params.opacity;
        this.meshs.forEach((mesh) => {
          mesh.setSymbol(this.material);
        });
      });
      gui.add(params, "altitude", 0, 300).onChange(() => {
        this.meshs.forEach((mesh) => {
          mesh.setAltitude(params.altitude);
        });
      });
      gui.add(params, "animateShow");
    },
    animateShow() {
      this.meshs.forEach((mesh) => {
        mesh.animateShow({
          duration: 3000,
        });
      });
    },
  },
  mounted() {
    this.init();
  },
};
</script>

<style>
#map {
  width: 100%;
  height: 100vh;
  position: relative;
  overflow: hidden;
  background-color: #000;
}
</style>